from xml.sax.saxutils import escape as xml_escape
from xml.sax.saxutils import unescape as xml_unescape
from xml.dom import minidom
from re import compile
from parser import Parser

class StringParser(Parser):
    '''
    This parser encapsulates all strings in a text with xml-markup
    and also carries out the inverse operation.
    '''
    
    modifier = ('u','U','r','R','ur','UR') #these modifiers can stand before strings
    
    def escape(self, text, **args):
        '''
        encapsulates all strings with xml tags of the form:
        <string><qualifier>"</qualifier><data>some text</data></string>
        '''
        
        #clear all escaped string qualifiers
        bufText = text.replace(r'\"','  ')
        
        #find positions of strings
        qualifierPos = self._getQualifierPos(bufText)
        limiterPos = self._getLimiterPos(qualifierPos)
        
        #encapsulate strings 
        lastEnd = 0
        buf = []
        for q, start, end in limiterPos:
            buf.append(text[lastEnd:start])
            s = xml_escape(text[start+len(q):end])
            buf.append("<string><qualifier>%s</qualifier><data>%s</data></string>"%(q,s))
            lastEnd = end + len(q)
        buf.append(text[lastEnd:])
        return ''.join(buf)
        
    def unescape(self, text, **args):
        '''
        reverses the operations of escape
        '''
        
        tagsPos = self._getTagsPos(text)
        lastEnd = 0
        buf = []
        for i in tagsPos:
            buf.append(text[lastEnd:i[0]])
            qualifier = text[i[1]:i[2]]
            data = text[i[3]:i[4]]
            buf.append(qualifier+
                       xml_unescape(data)+
                       qualifier.lstrip(''.join(self.modifier)))
            lastEnd = i[5]
        buf.append(text[lastEnd:])
        return ''.join(buf)
    
    def _getTagsPos(self, text):
        '''
        searches for xml tags of the form:
        <string><qualifier>"</qualifier><data>some text</data></string>
        and returns a list of start and end positions of tags in the form
        as follows.
        [(a, b, c, d, e, f),...]
        the positions that are returned are marked with '@':
        @<string><qualifier>@"@</qualifier><data>@some text@</data></string>@
        '''
        
        tagsPos = []
        end_string = 0
        while True:
            start_string = text.find('<string>', end_string)
            end_string = text.find('</string>', start_string)
            if start_string==-1 or end_string==-1:
                break
            start_qual = text.find('<qualifier>', start_string, end_string)
            end_qual = text.find('</qualifier>', start_qual, end_string)
            start_data = text.find('<data>', start_string, end_string)
            end_data = text.find('</data>', start_data, end_string)
            assert start_qual!=-1 and end_qual!=-1 and start_data!=-1 and end_data!=-1
            tagsPos.append((start_string,
                            start_qual+len('<qualifier>'),
                            end_qual,
                            start_data+len('<data>'),
                            end_data,
                            end_string+len('</string>')))
        return tagsPos
            
    def _getQualifierPos(self, text):
        '''
        searches all string qualifiers and returns their
        positions, modifiers and qualifiers in a list of the form
        [(modifier, qualifier, position),..]
        '''
    
        qualifierPos = []    
        def subst(qualifier):
            p = qualifier.start()
            m = ''
            q = qualifier.group()
            if p>0 and text[p-1] in self.modifier:
                m = text[p-1]
            qualifierPos.append((m, q, p))
            
        self.settings.pattern['string_qualifier'].sub(subst, text)
        return qualifierPos
    
    def _getLimiterPos(self, posList):
        '''
        runs through the list of string qualifiers and searchs
        start and end of strings, ignoring enclosed strings.
        returns a list of the form:
        [modifier+qualifier, startPosition, endPosition]
        '''
        
        limiter_pos = []
        start = ()
        for m, q, p in posList:
            if not start:
                start = (m+q, p)
                continue
            if q==start[0]:
                limiter_pos.append((start[0], start[1], p))
                start = ()
        return limiter_pos